#include "stdafx.h"

extern int g_iCasePassed;
extern int g_iCaseFailed;

#define _USE_MATH_DEFINES
#include <math.h>


///////////////////////////////////////////////////////////////////////
/// giFloat
///////////////////////////////////////////////////////////////////////

bool test_giFloat_logical()
{
    float x = (float)M_PI;
    giFloat f;
    f = x;
    return f==x 
        && !(f!=x)
        && f <= x
        && f >= x
        && f != x + giFloat::kEpsilon * 2
        && !( f == x + giFloat::kEpsilon * 2 )
        && f < x + giFloat::kEpsilon * 2
        && !( f >= x + giFloat::kEpsilon * 2)
        && f > x - giFloat::kEpsilon * 2
        && !( f <= x - giFloat::kEpsilon * 2);
}

bool test_giFloat_add()
{
    float x = (float)M_PI;
    float y = (float)M_PI_2;
    giFloat f1(x), f2(y);
    giFloat f3( f1 + f2 );
    giFloat f4 = f1; f4 += f2;

    return f3 == x+y && f3 == f4;
}

bool test_giFloat_minus()
{
    float x = (float)M_PI;
    float y = (float)M_PI_2;
    giFloat f1(x), f2(y);
    giFloat f3( f1 - f2 );
    giFloat f4 = f1; f4 -= f2;
    giFloat f5( f1 );
    f5 = -f5;

    return f3 == x-y && f3 == f4 && f5 == -x;
}

bool test_giFloat_multiple()
{
    float x = (float)M_PI;
    float y = (float)M_PI_2;
    giFloat f1(x), f2(y);
    giFloat f3( f1 * f2 );
    giFloat f4 = f1; f4 *= f2;

    return f3 == x*y && f3 == f4;
}

bool test_giFloat_divide()
{
    float x = (float)M_PI;
    float y = (float)M_PI_2;
    giFloat f1(x), f2(y);
    giFloat f3( f1 / f2 );
    giFloat f4 = f1; f4 /= f2;

    return f3 == x/y && f3 == f4;
}

bool test_giFloat_unary()
{
    float x = (float)M_PI;
    giFloat f1(x);
    return x == (float)(+f1) && -x == (float)-f1;
}

bool test_giFloat_prepostfix()
{
    float x = (float)M_PI;
    float y = x + 1;
    giFloat f1(x);
    giFloat f2(f1);

    if ( x != (float)(f2++) ) return false;
    if ( y != (float)f2 ) return false;

    if ( y != (float)(++f1) ) return false;

    return true;
}

void test_giFloat()
{
    printf ( "\n====== " __FUNCTION__ " ======\n" );
    V(test_giFloat_logical());
    V(test_giFloat_add());
    V(test_giFloat_minus());
    V(test_giFloat_multiple());
    V(test_giFloat_divide());
    V(test_giFloat_unary());
    V(test_giFloat_prepostfix());
}


///////////////////////////////////////////////////////////////////////
/// giDouble
///////////////////////////////////////////////////////////////////////

bool test_giDouble_logical()
{
    double x = M_PI;
    giDouble f;
    f = x;
    return f==x && !(f!=x) && f != x + giDouble::kEpsilon * 2 && !( f == x + giDouble::kEpsilon * 2 );
}

bool test_giDouble_add()
{
    double x = M_PI;
    double y = M_PI_2;
    giDouble f1(x), f2(y);
    giDouble f3( f1 + f2 );
    giDouble f4 = f1; f4 += f2;

    return f3 == x+y && f3 == f4;
}

bool test_giDouble_minus()
{
    double x = M_PI;
    double y = M_PI_2;
    giDouble f1(x), f2(y);
    giDouble f3( f1 - f2 );
    giDouble f4 = f1; f4 -= f2;
    giDouble f5( f1 );
    f5 = -f5;

    return f3 == x-y && f3 == f4 && f5 == -x;
}

bool test_giDouble_multiple()
{
    double x = M_PI;
    double y = M_PI_2;
    giDouble f1(x), f2(y);
    giDouble f3( f1 * f2 );
    giDouble f4 = f1; f4 *= f2;

    return f3 == x*y && f3 == f4;
}

bool test_giDouble_divide()
{
    double x = M_PI;
    double y = M_PI_2;
    giDouble f1(x), f2(y);
    giDouble f3( f1 / f2 );
    giDouble f4 = f1; f4 /= f2;

    return f3 == x/y && f3 == f4;
}

bool test_giDouble_unary()
{
    double x = M_PI;
    giDouble f1(x);
    return x == (double)(+f1) && -x == (double)-f1;
}

bool test_giDouble_prepostfix()
{
    double x = (double)M_PI;
    double y = x + 1;
    giDouble f1(x);
    giDouble f2(f1);

    if ( x != (double)(f2++) ) return false;
    if ( y != (double)f2 ) return false;

    if ( y != (double)(++f1) ) return false;

    return true;
}

void test_giDouble()
{
    printf ( "\n====== " __FUNCTION__ " ======\n" );
    V(test_giDouble_logical());
    V(test_giDouble_add());
    V(test_giDouble_minus());
    V(test_giDouble_multiple());
    V(test_giDouble_divide());
    V(test_giDouble_unary());
    V(test_giDouble_prepostfix());
}



///////////////////////////////////////////////////////////////////////
/// giFloat interact with giDouble
///////////////////////////////////////////////////////////////////////

bool test_giDouble_giFloat_logical()
{
    double x = M_PI;
    giFloat f((float)x);
    giDouble d(x);
    return f == d && d != f;    // d != f: because of the precision lost during conversion
}

void test_giDouble_giFloat()
{
    printf ( "\n====== " __FUNCTION__ " ======\n" );
    V(test_giDouble_giFloat_logical());
}


///////////////////////////////////////////////////////////////////////
/// Performance: giFloat and giDouble
///////////////////////////////////////////////////////////////////////

bool test_giReal_Perf_Arithmetic()
{
    float N=15000000;
#ifdef _DEBUG
    N /= 10;
#endif

    float c1 = 0;
    double t1 = giCurrentMillisecond();
    for( float i = 0; i < N; ++i )
    {
        float x = (float)i;
        x = x + (float)M_PI_2;      x += (float)M_PI_2;
        x = x * (float)M_E;         x *= (float)M_E;
        x = x - (float)M_PI_4;      x -= (float)M_PI_4;
        x = x / (float)M_LN2;       x /= (float)M_LN2;
        c1 += x;
    }

    double c2 = 0;
    double t2 = giCurrentMillisecond();
    for( double i = 0; i < N; ++i )
    {
        double x = i;
        x = x + M_PI_2;      x += M_PI_2;
        x = x * M_E;         x *= M_E;
        x = x - M_PI_4;      x -= M_PI_4;
        x = x / M_LN2;       x /= M_LN2;
        c2 += x;
    }

    float c3 = 0;
    double t3 = giCurrentMillisecond();
    for( float i = 0; i < N; ++i )
    {
        giFloat x = (float)i;
        x = x + (float)M_PI_2;      x += (float)M_PI_2;
        x = x * (float)M_E;         x *= (float)M_E;
        x = x - (float)M_PI_4;      x -= (float)M_PI_4;
        x = x / (float)M_LN2;       x /= (float)M_LN2;
        c3 += (float)x;
    }

    double c4 = 0;
    double t4 = giCurrentMillisecond();
    for( double i = 0; i < N; ++i )
    {
        giDouble x = i;
        x = x + M_PI_2;      x += M_PI_2;
        x = x * M_E;         x *= M_E;
        x = x - M_PI_4;      x -= M_PI_4;
        x = x / M_LN2;       x /= M_LN2;
        c4 += (double)x;
    }

    double t5 = giCurrentMillisecond();

    printf("- Arithmetic:\tfloat:\t\t%d\tdouble:\t\t%d\n\t\tgiFloat:\t%d\tgiDouble:\t%d\n", (int)(t2-t1), (int)(t3-t2), (int)(t4-t3), (int)(t5-t4) );
    return c1 == c3 && c2 == c4;
}

inline bool fuzzyEquals(float a, float b) { return fabs(a-b) < giFloat::kEpsilon; }
inline bool fuzzyLargerThan(float a, float b) { return a>b && !fuzzyEquals(a,b); }
inline bool fuzzyLargerThanEqual(float a, float b) { return a>b || fuzzyEquals(a,b); }

inline bool fuzzyEquals(double a, double b) { return fabs(a-b) < giDouble::kEpsilon; }
inline bool fuzzyLargerThan(double a, double b) { return a>b && !fuzzyEquals(a,b); }
inline bool fuzzyLargerThanEqual(double a, double b) { return a>b || fuzzyEquals(a,b); }

bool test_giReal_Perf_Relational()
{
    float N=15000000;
#ifdef _DEBUG
    N /= 10;
#endif

    float fVal = 1e5f;
    double dVal = 1e5;

    float c1 = 0, d1 = 0;
    double t1 = giCurrentMillisecond();
    for( float i = 0; i < N; ++i )
    {
        float x = i;
        c1 += ( x == fVal ) + ( x > fVal ) + ( x >= fVal );
        d1 += ( x != fVal ) + ( x < fVal ) + ( x <= fVal );
    }

    double c2 = 0, d2 = 0;
    double t2 = giCurrentMillisecond();
    for( double i = 0; i < N; ++i )
    {
        double x = i;
        c2 += ( x == dVal ) + ( x > dVal ) + ( x >= dVal );
        d2 += ( x != dVal ) + ( x < dVal ) + ( x <= dVal );
    }

    float c3 = 0, d3 = 0;
    double t3 = giCurrentMillisecond();
    for( float i = 0; i < N; ++i )
    {
        giFloat x(i);
        c3 += ( x == fVal ) + ( x > fVal ) + ( x >= fVal );
        d3 += ( x != fVal ) + ( x < fVal ) + ( x <= fVal );
    }

    double c4 = 0, d4 = 0;
    double t4 = giCurrentMillisecond();
    for( double i = 0; i < N; ++i )
    {
        giDouble x(i);
        c4 += ( x == dVal ) + ( x > dVal ) + ( x >= dVal );
        d4 += ( x != dVal ) + ( x < dVal ) + ( x <= dVal );
    }

    float c5 = 0, d5 = 0;
    double t5 = giCurrentMillisecond();
    for( float i = 0; i < N; ++i )
    {
        float x = i;
        c5 += fuzzyEquals(x,fVal) + fuzzyLargerThan(x,fVal) + fuzzyLargerThanEqual(x, fVal);
        d5 += !fuzzyEquals(x,fVal) + !fuzzyLargerThan(x,fVal) + !fuzzyLargerThanEqual(x, fVal);
    }

    double c6 = 0, d6 = 0;
    double t6 = giCurrentMillisecond();
    for( double i = 0; i < N; ++i )
    {
        double x = i;
        c6 += fuzzyEquals(x,dVal) + fuzzyLargerThan(x,dVal) + fuzzyLargerThanEqual(x, dVal);
        d6 += !fuzzyEquals(x,dVal) + !fuzzyLargerThan(x,dVal) + !fuzzyLargerThanEqual(x, dVal);
    }

    double t7 = giCurrentMillisecond();

    printf("- ControlFlow:\tfloat:\t\t%d\tdouble:\t\t%d\n\t\tgiFloat:\t%d\tgiDouble:\t%d\n\t\tfz float:\t%d\tfz double:\t%d\n",
        (int)(t2-t1), (int)(t3-t2), (int)(t4-t3), (int)(t5-t4), (int)(t6-t5), (int)(t7-t6) );
    return c1 == c3 && d1 == d3 && c2 == c4 && d2 == d4 && c3 == c5 && d3 == d5 && c4 == c6 && d4 == d6;
}

void test_giReal_Performance()
{
    printf ( "\n====== " __FUNCTION__ " ======\n" );

    V(test_giReal_Perf_Arithmetic());
    V(test_giReal_Perf_Relational());
    printf( "size: giFloat:%d, giDouble:%d\n", sizeof(giFloat), sizeof(giDouble) );
}

